package zookeep;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * @author yinchengfeng
 * @date 2022/3/9
 * @Description: 使用zookeeper注册
 */
@Slf4j
public class ZkRegister implements Watcher{

    /**应用程序根目录*/
    public static final String APP_ROOT_PATH = "/AppHome";
    private final ZooKeeper zk;
    CountDownLatch init = new CountDownLatch(1);

    private Map<String, List<AppConfig>> regServers;

    public ZkRegister(String zkHosts) throws IOException, InterruptedException {
        this.zk = new ZooKeeper(zkHosts, 3000, this);
        init.await();
    }

    /**
     * @param appName 一般为服务名
     * @param host 服务启动时的主机地址
     * @param port 对应的端口
     */
    public void register(String appName, String host, String port) {
        //检查并创建应用根节点
        createPathIfNoExist(APP_ROOT_PATH, "app root path", CreateMode.EPHEMERAL);
        addWatcher();
        //创建服务名的节点，并在其下创建一个有序的临时节点，表示当前服务。
//        createServerNode(appName, host, port);
        AppConfig config = new AppConfig(appName, host, port);
        //从根节点开始，重新合并路径
        if (appName == null || "".equals(appName)) {
            log.error("[createServerNode][" + " appName :" + appName + "; host :" + host + "; port :" + port + "][error][未设置服务名]");
            return;
        }
        String fullPath = getFullPath(APP_ROOT_PATH, appName);
        createPathIfNoExist(fullPath, config.toString(), CreateMode.EPHEMERAL_SEQUENTIAL);

        this.regServers = getRegServersFromZk();
        printRegServers();
    }

    public  Map<String, List<AppConfig>> getRegServers(){
        return regServers;
    }

    private void addWatcher() {
        try {
            Stat exists = zk.exists(APP_ROOT_PATH, this);
            if(exists==null){
                System.out.println(APP_ROOT_PATH + "添加watch失败");
            }else{
                System.out.println(APP_ROOT_PATH + ",创建watch成功:" + exists);
            }
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取fullPath路径下所有注册的服务     *
     * @return 所有注册的服务
     */
    protected Map<String, List<AppConfig>> getRegServersFromZk() {
        Map<String, List<AppConfig>> map = new HashMap<>(16);
        List<String> children = null;
        try {
            children = zk.getChildren(ZkRegister.APP_ROOT_PATH, this);
        }catch (InterruptedException | KeeperException e) {
            e.printStackTrace();
        }
        if(children==null){
            return map;
        }
        children.forEach(str -> {
            String subFullPath = getFullPath(ZkRegister.APP_ROOT_PATH, str);
            try {
                byte[] data = zk.getData(subFullPath, false, new Stat());
                String jsonData = new String(data, StandardCharsets.UTF_8);
                System.out.println("subDate-" + subFullPath + ":" + jsonData);
                AppConfig appConfig = JSONObject.parseObject(jsonData, AppConfig.class);
                if (appConfig != null) {
                    List<AppConfig> appConfigs = map.get(appConfig.appName);
                    if (appConfigs == null) {
                        appConfigs = new ArrayList<>();
                        map.put(appConfig.getAppName(), appConfigs);
                    }
                    appConfigs.add(appConfig);
                }
            } catch (KeeperException | InterruptedException e) {
                e.printStackTrace();
            }
        });
        return map;
    }

    protected String getFullPath(String appRootPath, String appName) {
        if (appName.contains(File.separator)) {
            String[] split = appName.split(File.separator);
            int size = split.length;
            if (size == 0) {
                return appRootPath;
            }
            for (int i = size - 1; i > -1; i--) {
                if (!"".equals(split[i])) {
                    appName = split[i];
                    break;
                }
            }
        }
        return appRootPath + "/" + appName;
    }

    private void createPathIfNoExist(String path, String data, CreateMode createMode){
        Stat exists = null;
        try {
            exists = zk.exists(path, this);
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
        if (exists == null) {
            try {
                //不存节点，创建节点
                zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public void process(WatchedEvent event) {
        if(init.getCount()>0) {
            init.countDown();
            System.out.println("连接成功！");
        }
        System.out.println("当前事件状态：" + event.getState());
        final String path = event.getPath();
        System.out.println("当前事件Path：" + path);
        System.out.println("当前事件Type：" + event.getType());
        switch (event.getState()) {
            case Unknown:
            case Disconnected:
            case NoSyncConnected:
                break;
            case SyncConnected:
                init.countDown();
                break;
            case AuthFailed:
            case Expired:
            case ConnectedReadOnly:
            case SaslAuthenticated:
            default:
        }
        if(APP_ROOT_PATH.equals(path)){
            addWatcher();
        }

        this.regServers = getRegServersFromZk();
        printRegServers();
    }

    public void printRegServers(){
        if(regServers!=null){
            Set<String> keys = regServers.keySet();
            for (String key : keys) {
                List<AppConfig> appConfigs = regServers.get(key);
                if(appConfigs!=null){
                    appConfigs.forEach(appConfig -> System.out.println(appConfig.toString()));
                }
            }
        }
    }
}
